package ongd.negocio.gestion;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import ongd.negocio.sesion.ControladorSesion;
import ongd.servicios.persistencia.ControladorPersistencia;
import ongd.servicios.persistencia.exceptions.IllegalOrphanException;
import ongd.servicios.persistencia.exceptions.NonexistentEntityException;

/**
 * Clase que realizará las operaciones necesarias para gestionar los padrinos
 * en la base de datos.
 *
 * @author <a href="mailto:egi0002@alu.ubu.es">Elena García Iglesias</a>
 * @author <a href="mailto:cms0021@alu.ubu.es">Cristina Martínez Sanz</a>
 * @version 1.0 20120124
 */
public class ControladorPadrino extends ControladorPersona {

    /**
     * Constructor de la clase.
     * @param contSesion session en la que estamos
     * @param contP controlador persistencia
     */
    public ControladorPadrino(ControladorPersistencia contP, ControladorSesion contSesion) {
        super(contP, contSesion);
    }

    /**
     * Método que creará un padrino en función de los parámetros
     * pasados.
     * @param persona socio o padrino que vamos a crear
     * @param listaF Lista de fracciones que tendrá dicha persona
     * @param proyecto proyecto al que se asociará un padrino exclusivamente
     * @param apadrinado proyecto al que se asociará un padrino exclusivamente
     * @return cadena indicando si la operación se ha realizado correctamente
     * @throws IllegalOrphanException 
     * @throws NonexistentEntityException 
     * @throws Exception 
     */
    public String crearPadrino(Persona persona, List<Fraccionado> listaF, Proyecto proyecto, Apadrinado apadrinado) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (buscarPersona(persona.getDni()) == null) {
            if (persona.esPadrino() && persona.getDni() != null && persona.getNombre() != null && persona.getApellidos() != null
                    && persona.getCantidad() != null && persona.getDomiciliacion() != null && proyecto != null && !listaF.isEmpty()) {
                if (!persona.getDomiciliacion() || (persona.getDomiciliacion() && persona.getNCuenta() != null)) {
                    if (proyecto.esProyectoApadrinamientoGeneral() || ((proyecto.esProyectoApadrinamientoPersonal() && apadrinado != null) && apadrinado.getPersonaDNI() == null)) {
                        //Inicio la transacción con la base de datos
                        getContP().iniciarTransaccion();
                        //Establezco la fecha de registro al diá actual
                        persona.setFechaRegistro(new Date());
                        List<Proyecto> listaP = new ArrayList<Proyecto>();
                        listaP.add(proyecto);
                        persona.setProyectoList(listaP);
                        getContP().editarEntidad(proyecto);
                        if (proyecto.esProyectoApadrinamientoPersonal()) {
                            List<Apadrinado> listaA = new ArrayList<Apadrinado>();
                            listaA.add(apadrinado);
                            persona.setApadrinadoList(listaA);
                            getContP().editarEntidad(apadrinado);
                        }
                        getContP().crearEntidad(persona);
                        //crearemos los fraccionados con las cuotas correspondientes.
                        crearFraccionados(persona, listaF);
                        crearCuotas(persona, persona.getCantidad());
                        getContP().terminarTransaccion();
                        return "Correcto";
                    }
                }
            }
        } else {
            return "Repetido";
        }
        return "Incorrecto";
    }

    /**
     * Método que edita un padrino en función de los parámetros pasados.
     * @param padrino Padrino a editar
     * @param listaF Lista de fracciones
     * @param listaP Lista de proyectos
     * @param listaA Lista de apadrinados
     * @return true si la operación se ha realizado correctamente
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception 
     */
    public boolean editarPadrino(Persona padrino, List<Fraccionado> listaF, List<Proyecto> listaP, List<Apadrinado> listaA) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (padrino.esPadrino() && padrino.getDni() != null && padrino.getNombre() != null && padrino.getApellidos() != null
                && padrino.getCantidad() != null && padrino.getDomiciliacion() != null) {
            if (!padrino.getDomiciliacion() || (padrino.getDomiciliacion() && padrino.getNCuenta() != null)) {
                //Inicio la transacción
                getContP().iniciarTransaccion();
                if (listaF == null) {
                    //Edito el socio, considerando que la lista del mismo no ha cambiado.
                    editarPagoFraccionado(padrino.getFraccionadoList());
                    editarCuota(padrino);
                } else {
                    //si la listaF no está vacia, implica que hemos cambiado el pago fraccionado
                    //borraré el pago fraccionado que tenia antes.
                    borrarPagoFraccionado(padrino.getFraccionadoList());
                    //borraré las cuotas pendientes que tenia el socio.
                    double cantidadAbonada = borrarCuotasPendientes(padrino.getCuotaList());
                    getContP().terminarTransaccion();
                    getContP().iniciarTransaccion();
                    //crearé el nuevo pago fraccionado a partir de la lista.
                    crearFraccionados(padrino, listaF);
                    //Calculo la cantidad que se debe de abonar
                    double cantidad = padrino.getCantidad() - cantidadAbonada;
                    //Si la cantidad es mayor que 0 creo las nuevas cuotas
                    if (cantidad > 0) {
                        //obtengo las nuevas cuotas necesarias
                        crearCuotas(padrino, cantidad);
                    }
                }
                if (listaP == null && listaA == null) {
                    //recorreré la lista para ir editando los proyectos
                    for (Proyecto p : padrino.getProyectoList()) {
                        getContP().editarEntidad(p);
                    }
                    for (Apadrinado a : padrino.getApadrinadoList()) {
                        getContP().editarEntidad(a);
                    }
                } else if (listaP != null && listaA != null) {
                    //Elimino la relacion entre los proyectos en los que cooperaba
                    eliminarRelacionPadrino_Proyectos(padrino);
                    //Elimino la relacion que existia con los apadrinados correspondientes
                    eliminarRelacionApadrinado_Padrino(padrino);
                    getContP().terminarTransaccion();
                    getContP().iniciarTransaccion();
                    //Creo la relacion con los proyectos que tiene la lista pasada por parámetro
                    crearCooperacionProyectos(padrino, listaP);
                    //Creo la relacion con los apadrinados que tiene la lista pasada por parámetro
                    crearCooperacionApadrinados(padrino, listaA);
                }
                //Edito el padrino
                getContP().editarEntidad(padrino);
                //Finalizo la transacción
                getContP().terminarTransaccion();
                return true;
            }
        }
        return false;
    }

    /**
     * Método que elimina la relacion existente entre un padrino y todos los
     * proyectos con los que colabora.
     * @param padrino Padrino al que quitaremos la relación con los socios
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception
     */
    public void eliminarRelacionPadrino_Proyectos(Persona padrino) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (padrino.esPadrino()) {
            List<Proyecto> listaP = padrino.getProyectoList();
            //Recorro la lista de proyectos para ir quitando al padrino de la lista de personas del proyecto
            for (Proyecto p : listaP) {
                List<Persona> listaPadrinos = p.getPersonaList();
                //Para quitar el padrino de la lista de personas,, recorreré la lista e iré comprobando
                for (int i = listaPadrinos.size() - 1; i >= 0; i--) {
                    //Si es el padrino le elimino de la lista y edito el proyecto
                    if (listaPadrinos.get(i).getDni().compareTo(padrino.getDni()) == 0) {
                        listaPadrinos.remove(i);
                        getContP().editarEntidad(p);
                    }
                }
            }
            padrino.setProyectoList(new ArrayList<Proyecto>());
        }
    }

    /**
     * Método que elimina la relación existente entre un padirno y los
     * apadrinados con los que colabora.
     * @param padrino Padrino
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception
     */
    public void eliminarRelacionApadrinado_Padrino(Persona padrino) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (padrino.esPadrino()) {
            List<Apadrinado> listaA = padrino.getApadrinadoList();
            //Recorreré la lista para ir poniendo a cada apadrinado que el dni del padrino es nulo
            for (Apadrinado apadrinado : listaA) {
                apadrinado.setPersonaDNI(null);
                getContP().editarEntidad(apadrinado);
            }
        }
        padrino.setApadrinadoList(new ArrayList<Apadrinado>());
    }

    /**
     * Método que crea la relación entre un padrino y una lista de proyectos.
     * @param padrino Padrino
     * @param listaP Lista de proyectos que relacionaremos con un padrino
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception
     */
    public void crearCooperacionProyectos(Persona padrino, List<Proyecto> listaP) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (padrino.esPadrino()) {
            for (Proyecto p : listaP) {
                //Recorro la lista e iré relacionando el padrino al proyecto
                p.getPersonaList().add(padrino);
                getContP().editarEntidad(p);
            }
            padrino.setProyectoList(listaP);
        }
    }

    /**
     * Método que crea la relación entre un padrino y una lista de apadriandos.
     * @param padrino Padrino
     * @param listaA Lista de apadrinados que relacionaremos con el padrino
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception
     */
    public void crearCooperacionApadrinados(Persona padrino, List<Apadrinado> listaA) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (padrino.esPadrino()) {
            for (Apadrinado a : listaA) {
                //Recorro la lsita e iré relacionando al padrino con el apadrinado
                a.setPersonaDNI(padrino);
                getContP().editarEntidad(a);
            }
            padrino.setApadrinadoList(listaA);
        }
    }

    /**
     * Método que borra un padrino.
     * @param persona Padrno a borrar
     * @return true si la operación se ha realizado 
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception 
     */
    @Override
    public boolean borrarPersona(Persona persona) throws IllegalOrphanException, NonexistentEntityException, Exception {
        boolean borrado = false;
        if (getContP().obtenerEntidad(Persona.class, persona.getDni()) != null) {
            if (persona.esPadrino()) {
                //Miraré si el padrino tiene cuotas abonadas, ya qeu si es así no podré borrarle
                boolean abonadas = false;
                for (Cuota c : persona.getCuotaList()) {
                    if (c.esAbonada() && abonadas == false) {
                        abonadas = true;
                    }
                }
                //Si no tiene cuotas abonadas, borro el padrino
                if (!abonadas) {
                    //Inicio la transacción
                    getContP().iniciarTransaccion();
                    borrarPagoFraccionado(persona.getFraccionadoList());
                    borrarCuotas(persona.getCuotaList());
                    eliminarRelacionPadrino_Proyectos(persona);
                    eliminarRelacionApadrinado_Padrino(persona);
                    getContP().destroyEntidad(Persona.class, persona.getDni());
                    //Termino la transacción
                    getContP().terminarTransaccion();
                    borrado = true;
                }
            }
        }
        return borrado;
    }

    /**
     * Método que da de baja un padrino.
     * @param persona padrino que daremos de baja
     * @return true si la operación se ha realizado correctamente
     * @throws IllegalOrphanException 
     * @throws NonexistentEntityException 
     * @throws Exception 
     */
    @Override
    public boolean darBajaPersona(Persona persona) throws IllegalOrphanException, NonexistentEntityException, Exception {
        if (persona.esPadrino()) {
            //Si la persona existe
            if (getContP().obtenerEntidad(Persona.class, persona.getDni()) != null) {
                if (persona.getFechaBaja() == null) {
                    //Pongo la fecha de baja del socio a la fecha actual
                    persona.setFechaBaja(new Date());
                    //Inicio la transacción
                    getContP().iniciarTransaccion();
                    eliminarRelacionPadrino_Proyectos(persona);
                    eliminarRelacionApadrinado_Padrino(persona);
                    getContP().editarEntidad(persona);
                    cancelarCuotasPendientes(persona.getCuotaList());
                    //Termino la transacción
                    getContP().terminarTransaccion();
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Método que da de alta una persona en la base de datos.
     * @param persona Persona que vamos a dar de alta
     * @param proyecto Proyecto que asociaremos al padrino
     * @param apadrinado Apadrinado que asociaremos al padrino
     * @return true o false en función de si la operación se ha realizado
     * correctamente
     * @throws IllegalOrphanException
     * @throws NonexistentEntityException
     * @throws Exception
     */
    public boolean darAltaPersona(Persona persona, Proyecto proyecto, Apadrinado apadrinado) throws IllegalOrphanException, NonexistentEntityException, Exception {
        boolean altaOK = false;
        if (getContP().obtenerEntidad(Persona.class, persona.getDni()) != null) {
            if (proyecto != null) {
                if (proyecto.esProyectoApadrinamientoPersonal() && apadrinado != null) {
                    if (apadrinado.getPersonaDNI() == null) {
                        Calendar fechaActual = Calendar.getInstance();
                        int anno = fechaActual.get(Calendar.YEAR);
                        //Obtengo las cuotas que tiene ese socio
                        List<Cuota> cuotasAnuales = obtenerCuotasAnuales(persona, anno);
                        //Si la lista devuelta es vacia crearé las cuotas correspondientes
                        //Iniciamos las transacción
                        getContP().iniciarTransaccion();
                        if (cuotasAnuales.isEmpty()) {
                            crearCuotas(persona, persona.getCantidad());
                        } else {
                            //Miraré a ver si existe alguna cuota abonada de las cuotas anuales
                            double cantidadAbonada = 0;
                            for (Cuota c : cuotasAnuales) {
                                if (c.esAbonada()) {
                                    cantidadAbonada += c.getCantidad();
                                }
                            }
                            //Obtengo la cantidad pendiente a pagar
                            double cantidadPendiente = persona.getCantidad() - cantidadAbonada;
                            if (cantidadPendiente > 0) {
                                //Obtengo las cuotas que hay canceladas a partir de la fecha actual
                                List<Cuota> cuotasCanceladas = obtenerCuotasCanceladas(cuotasAnuales);
                                //Establezco esas cuotas a pendientes y cambio la cantidad por lo que corresponda
                                establecerCuotasPendientes(cuotasCanceladas, cantidadPendiente);
                            }
                        }
                        List<Proyecto> listaP = new ArrayList<Proyecto>();
                        listaP.add(proyecto);
                        persona.setProyectoList(listaP);
                        getContP().editarEntidad(proyecto);
                        if (proyecto.esProyectoApadrinamientoPersonal()) {
                            List<Apadrinado> listaA = new ArrayList<Apadrinado>();
                            listaA.add(apadrinado);
                            persona.setApadrinadoList(listaA);
                            apadrinado.setPersonaDNI(persona);
                            getContP().editarEntidad(apadrinado);
                        }
                        persona.setFechaBaja(null);
                        getContP().editarEntidad(persona);
                        //Finalizo la transacción
                        getContP().terminarTransaccion();
                        altaOK = true;
                    }
                }
            }
        }
        return altaOK;
    }
}
